#ifndef MODELOESTRUCTURADO_H_
#define MODELOESTRUCTURADO_H_

#include "includes.h"
#include "Aritmetico.cpp"
#include "Nivel.cpp"
#include "compressedStream.cpp"
#include "ModeloEstructurado.h"

void ModeloEstructurado::setInputFilename(const char* filename) {
	this->inputFilename = (char*)filename;
}

void ModeloEstructurado::setOutputFilename(const char* filename) {
	this->outputFilename = (char*)filename;
}

void* ModeloEstructurado::getNivelActual() {
	return ((this->niveles).at(this->IDnivelActual));
}

void ModeloEstructurado::crearNiveles() {
	unsigned short i;
	for (i = 0; i < (this -> cantidadNiveles); i++) {
		Nivel* nivel = new Nivel(i);
		(this -> niveles).push_back (nivel);
	}
}

/*
 * 
 * COMPRESOR
 * 
 */

ModeloEstructuradoCompress::ModeloEstructuradoCompress() {
	
	this -> IDnivelActual = 0;
	this -> readingFromFile = false;
	
	crearNiveles();
}

ModeloEstructuradoCompress::ModeloEstructuradoCompress(const char* inputFilename, uInt blockSize) {
	
	this -> readingFromFile = true;
	this -> blocksize = blockSize;
	this -> IDnivelActual = 0;
	this -> imAtFirstBlock  = true;
	
	this -> setInputFilename(inputFilename);
	this -> setOutputFilename("ModeloEstructuradoCompresorOutput.txt");
	
	crearNiveles();
}

ModeloEstructuradoCompress::~ModeloEstructuradoCompress() {
	
	unsigned int i;
	for (i = 0; i < (this->cantidadNiveles); i++) {
		delete(this->niveles.at(i));
	}
	
	this->niveles.clear();
	
	if( this->inputFile.is_open() ){
		this->inputFile.close();
	}

	if( this->readingFromFile == true ){
		delete this->outputFile;
	}

}

void ModeloEstructuradoCompress :: comprimirChar (char caracter, bool isEof ) {
	this->IDnivelActual = 0;
	Nivel* nivelActual = this->niveles.at(this->IDnivelActual);
	
	if (nivelActual->tieneCaracter ( caracter )) {
		if(this->debug) cout << "Comprimimos " << caracter << " en nivel " << IDnivelActual << endl;
		nivelActual->comprimirChar( caracter, isEof);
	} else {
		while ( ! nivelActual->tieneCaracter (caracter)) {
			if (this->IDnivelActual == 0) {
				// Emitimos escape en el nivel 0
				if(this->debug) cout << "Comprimimos escape en nivel " << IDnivelActual << endl;
				nivelActual->comprimirChar((uChar)1);
				}
			else {
				// Emitimos escape en los demas niveles
				if(this->debug) cout << "Comprimimos escape en nivel " << IDnivelActual << endl;
				nivelActual->comprimirChar((uChar)0);
				}
				
			this->IDnivelActual ++;
			nivelActual = this->niveles.at(this->IDnivelActual);
		}
		
		if(this->debug) cout << "Comprimimos " << caracter << " en nivel " << IDnivelActual << endl;
		nivelActual->comprimirChar( caracter, isEof );
		
		this->IDnivelActual = 0;
		nivelActual = this->niveles.at(this->IDnivelActual);
	}
	
}

int ModeloEstructuradoCompress :: runBlock(const string input, vector<string>* output) {
	
	if ( input.length() <= 0) {
		return -1;
	}
	
	unsigned int i;
	for (i = 0; i < input.length(); i++) {
		char caracter = input.at(i);
		bool isEof = (caracter == EOF);
		comprimirChar(caracter, isEof);
	}

	// Recorremos todos los niveles y concatenamos en "output"
	// los buffers de cada compresor aritmetico
	
	uInt j;
	for (j = 0; j < cantidadNiveles; j++) {
		string resultadoParcial;
		// no estoy segura de la linea que sigue (el 2do parametro)
		this -> niveles.at(j) -> getSalidaComprimida(&resultadoParcial, readingFromFile);
		cout << "Tamanio del Resultado parcial en nivel " << j << ": " << resultadoParcial.length() << endl;
		if ( ! resultadoParcial.empty()) {
			output->push_back( resultadoParcial );
		}
	}
	
	return 0;
}

int ModeloEstructuradoCompress::run() {
	
	int status = 0;
	vector<string>* v = new vector<string>;
	while (status != ERROR_FILE_NOT_EXIST && status != ERROR_EOF && status != ERROR_LECTURA) {
		v->clear();
		status = runNextBlock(v);
	}
	delete(v);
	return 0;
}

// Cada posicion del vector tiene un puntero a un string que es el resultado
// de cada nivel (de 0 a 7)
int ModeloEstructuradoCompress::runNextBlock(vector<string>* output){
	
	if( this->imAtFirstBlock == true ){
		this->imAtFirstBlock = false;
		this->outputFile = new OCompressedStream( this->outputFilename , this->blocksize );
	}
	
	uInt status;
	char* input = (char*)malloc( this->blocksize * sizeof(char) );
	
	if (input != NULL) {
	
		status = this->getNextBlockOfText(&input);
		
		if( status == ERROR_LECTURA ){
			cout << "Error de lectura." << endl;
			free(input);
			return ERROR_LECTURA;

		}
		if( status == ERROR_EOF ){
			cout << "End of file." << endl;
			free(input);
			return ERROR_EOF;
		}

		this->runBlock(input, output);
		this->outputFile->writeModeloEstructuradoResult( output );
		
		free(input);
		
		return 0;
	} else {
		return -1;
	}
	
}

int ModeloEstructuradoCompress::getNextBlockOfText(char** text){
	if( ! this->inputFile.is_open() ){
		this->inputFile.open( this->inputFilename , ios::binary );
		if( ! this->inputFile.is_open() ) {
			return ERROR_FILE_NOT_EXIST;
		}
		this->readingFromFile = true;
	}

	if( this->inputFile.eof() ){
		return ERROR_EOF;
	}
	
	this->inputFile.read( *text , this->blocksize );
	
	if( this->inputFile.bad() ){
		return ERROR_LECTURA;
	}
	
	return 0;
}

/*
 * 
 * DESCOMPRESOR
 * 
 */
 
ModeloEstructuradoDecompress::ModeloEstructuradoDecompress() {
	
	this -> IDnivelActual = 0;
	this -> readingFromFile = false;
	
	crearNiveles();
}

ModeloEstructuradoDecompress::ModeloEstructuradoDecompress(const char* inputFilename, uInt blockSize) {
	
	this -> readingFromFile = true;
	this -> blocksize = blockSize;
	this -> IDnivelActual = 0;
	//this -> imAtFirstBlock  = true;
	
	this -> setInputFilename(inputFilename);
	this -> setOutputFilename("ModeloEstructuradoDescompresorOutput.txt");
	
	crearNiveles();
}

ModeloEstructuradoDecompress::~ModeloEstructuradoDecompress() {
	
	unsigned int i;
	for (i = 0; i < (this->cantidadNiveles); i++) {
		delete(this->niveles.at(i));
	}
	
	this->niveles.clear();
	
	if( this->readingFromFile ){
		delete this->inputFile;
	}

	if( this->outputFile.is_open() ){
		this->outputFile.close();
	}
}

void ModeloEstructuradoDecompress::decompressString(const string* input) {
	
	// TO DO
	
}

int ModeloEstructuradoDecompress::runBlock(const char* textoComprimido, string* textoDescomprimido) {
	
	// TO DO
	return 0;
	
}

int ModeloEstructuradoDecompress::run() {
	
	// TO DO
	return 0;
}

int ModeloEstructuradoDecompress::runNextBlock( string* textoDescomprimido ) {
	
	// TO DO
	return 0;
}


#endif	/* MODELOESTRUCTURADO_H */
